<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>5.8.1.2-如何代理Set和Map</title>
</head>

<body>
    <div id="container"></div>
</body>
    <script>
    /**
     * 尝试从 Set 中删除数据
     * - 调用 p.delete 方法时会得到一个错误， 
     *   这个错误与前文讲解的访问 p.size 属性时发生的错误非常相似
     *   (TypeError: Method Set.prototype.delete called on incompatible receiver [object Object])
     * 
     * 
     * 访问 p.size 与访问 p.delete 是不同的
     * - size 是属性，是一个访问器属性，而 delete 是一个方法
     * - 访问 p.size 时，访问器属性的 getter 函数会立即执行，
     *    此时我们可以通过修改receiver 来改变 getter 函数的 this 的指向。
     * - 当访问 p.delete 时，delete 方法并没有执行，真正使其执行的语句是 p.delete(1) 这句函数调用
     *    无论怎么修改 receiver，delete 方法执行时的 this 都会指向代理对象 p，而不会指向原始 Set 对象。
     * 
     * 修复这个问题
     * - 使用 bind 函数将用于操作数据的方法与原始数据对象 target 做了绑定。
     * - 当p.delete(1) 语句执行时，delete 函数的 this 总是指向原始数据对象而非代理对象
     */

    const s = new Set([1, 2, 3])
    const p = new Proxy(s, {
        get(target, key, receiver) {
            if (key === 'size') {
                // 如果读取的是 size 属性
                // 通过指定第三个参数 receiver 为原始对象 target 从而修复问题
                return Reflect.get(target, key, target)
            }
            // 读取其他属性的默认行为
            // return Reflect.get(target, key, receiver)
            // 将方法与原始数据对象 target 绑定后返回
            return target[key].bind(target)
        }
    })
    
    // 调用 delete 方法删除值为 1 的元素
    // 会得到错误 TypeError: Method Set.prototype.delete called on incompatible receiver [object Object]
    p.delete(1)
    console.log(p)
</script>

</html>